<?php
/**
 * Author:xsc
 * Time:2023/9/5 19:31
 */

namespace App\Extensions\Common;

use App\Common\ResponseCode;
use App\Exceptions\BusinessException;
use App\Extensions\BaseService;
use \Redis;

class RedisService extends BaseService
{

    private $redis;

    private $host;

    private $port;

    public function __construct()
    {
        $this->host = env('REDIS_HOST');
        $this->port = env('REDIS_PORT');

        $this->redis = new Redis();
        $this->redis->connect($this->host, $this->port);
        $this->redis->auth(env('REDIS_PASSWORD'));
        return $this->redis;
    }

    /**
     * 访问控制,多少秒允许访问一次
     * @param $key
     * @param $left_time
     * @param $db_index
     * @return void
     * @throws BusinessException
     * @throws \RedisException
     */
    public function accessControl($key, $left_time, $db_index = 2)
    {
        $this->redis->select($db_index);

        $result = $this->redis->set($key, 1, ['nx', 'ex' => $left_time]);

        if (!$result) {
            throw new BusinessException(ResponseCode::REPEAT_OPERATION);
        }
    }

    //redis 键(key)命令

    /**
     * 删除已存在的键,不存在的key会被忽略
     * @param $key
     * @param $db_index
     * @return false|int|Redis
     * @throws \RedisException
     */
    public function del($key, $db_index = 2)
    {
        $this->redis->select($db_index);
        return $this->redis->del($key);
    }

    //redis 字符串(string)命令

    /**
     * 设置一个key的value值
     * @param $key
     * @param $value
     * @param $left_time
     * @param $db_index
     * @return bool|Redis
     * @throws \RedisException
     */
    public function set($key, $value, $left_time = 0, $db_index = 2)
    {
        $this->redis->select($db_index);
        $result = $this->redis->set($key, $value);
        if ($result && $left_time) {
            $this->redis->expire($key, $left_time);
        }
        return $result;
    }

    /**
     * 返回key的value
     * @param $key
     * @param $db_index
     * @return false|mixed|Redis|string
     * @throws \RedisException
     */
    public function get($key, $db_index = 2)
    {
        $this->redis->select($db_index);
        return $this->redis->get($key);
    }

    //redis 列表(List)命令

    /**
     * 获取列表长度
     * @param $key
     * @param $db_index
     * @return bool|int|\Redis
     * @throws \RedisException
     */
    public function lLen($key, $db_index = 2)
    {
        $this->redis->select($db_index);
        return $this->redis->lLen($key);
    }

    /**
     * 通过索引获取列表中的元素
     * @param $key
     * @param $index
     * @param $db_index
     * @return bool|mixed|\Redis
     * @throws \RedisException
     */
    public function lIndex($key, $index, $db_index = 2)
    {
        $this->redis->select($db_index);
        return $this->redis->lIndex($key, $index);
    }

    /**
     * 移除列表中与参数 VALUE 相等的元素
     * @param $key
     * @param $value
     * @param $count
     * @param $db_index
     * @return bool|int|Redis
     * @throws \RedisException
     */
    public function lRem($key, $value, $count = 0, $db_index = 2)
    {
        $this->redis->select($db_index);
        return $this->redis->lRem($key, $value, $count);
    }

    /**
     *  命令用于将一个或多个值插入到列表的尾部(最右边)。
     *  如果列表不存在，一个空列表会被创建并执行 RPUSH 操作。 当列表存在但不是列表类型时，返回一个错误。
     * @param $key
     * @param $value
     * @param $left_time
     * @param $db_index
     * @return false|int|Redis
     * @throws \RedisException
     */
    public function rPush($key, $value, $left_time = 0, $db_index = 2)
    {
        $this->redis->select($db_index);

        // key已经存在就不设置过期时间,防止永不过期
        if ($this->redis->exists($key)) {
            $left_time = 0;
        }

        $result = $this->redis->rPush($key, $value);

        if ($result && $left_time) {
            $this->redis->expire($key, $left_time);
        }

        return $result;
    }

    /**
     * 移除并返回列表的第一个元素。
     * @param $key
     * @param $db_index
     * @return bool|mixed|Redis
     * @throws \RedisException
     */
    public function lPop($key, $db_index = 2)
    {
        $this->redis->select($db_index);
        return $this->redis->lPop($key);
    }

    /**
     * 获取列表指定范围内的元素
     * @param $key
     * @param $start
     * @param $end
     * @param $db_index
     * @return array|Redis
     * @throws \RedisException
     * Author:xsc
     * Time:2023/10/10 15:09
     */
    public function lRange($key, $start = 0, $end = -1, $db_index = 2)
    {
        $this->redis->select($db_index);
        return $this->redis->lRange($key, $start, $end);
    }

    //redis 哈希(hash)命令

    /**
     * 删除哈希表 key 中的一个或多个指定字段，不存在的字段将被忽略。
     * @param $key
     * @param $hash_key
     * @param $db_index
     * @return bool|int|\Redis
     * @throws \RedisException
     */
    public function hDel($key, $hash_key, $db_index = 2)
    {
        $this->redis->select($db_index);
        return $this->redis->hDel($key, $hash_key);
    }

    /**
     * 设置hash里面一个字段的值
     * @param $key
     * @param $hash_key
     * @param $value
     * @param $left_time
     * @param $db_index
     * @return bool|int|\Redis
     * @throws \RedisException
     */
    public function hSet($key, $hash_key, $value, $left_time = 0, $db_index = 2)
    {
        $this->redis->select($db_index);

        //key已经存在就不设置过期时间,防止永不过期
        if ($this->redis->exists($key)) {
            $left_time = 0;
        }

        $result = $this->redis->hSet($key, $hash_key, $value);

        if ($result && $left_time) {
            $this->redis->expire($key, $left_time);
        }

        return $result;
    }

    /**
     * 从hash中读取全部的域和值
     * @param $key
     * @param $db_index
     * @return array|false|Redis
     * @throws \RedisException
     * Author:xsc
     * Time:2023/10/10 15:05
     */
    public function hGetAll($key, $db_index = 2)
    {
        $this->redis->select($db_index);
        return $this->redis->hGetAll($key);
    }

}
